﻿using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace Cxuu.Common
{
    /// <summary>
    /// 标注要运用DI的类 被此属性标注的类 要被注册到依赖注入容器中 并且可以指定类要映射的接口或者类
    /// 此属性只能运用于类，并且此属性不能继承
    /// </summary>
    [AttributeUsage(AttributeTargets.Class, Inherited = false)]
    public class UseDIAttribute : Attribute
    {
        //Targets用于指示 哪些接口或者类 要被 "被属性修饰了的类" 进行依赖注入
        private List<Type> targetTypes = new List<Type>();
        private readonly ServiceLifetime lifetime;

        public UseDIAttribute(ServiceLifetime lifetime, params Type[] argTargets)
        {
            this.lifetime = lifetime;
            foreach (var argTarget in argTargets)
            {
                targetTypes.Add(argTarget);
            }
        }

        public List<Type> GetTargetTypes()
        {
            return targetTypes;
        }

        public ServiceLifetime Lifetime
        {
            get
            {
                return this.lifetime;
            }
        }
    }

    public static class AutoRegisterServiceCollectionExtensions
    {
        /// <summary>
        /// 自动注册服务
        /// </summary>
        /// <param name="services">注册服务的集合（向其中注册）</param>
        /// <param name="implementationType">要注册的类型</param>
        public static void AutoRegisterService(this IServiceCollection services, Type implementationType)
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services) + "为空");

            if (implementationType == null)
                throw new ArgumentNullException(nameof(implementationType) + "为空");

            //获取类型的 UseDIAttribute 属性 对应的对象
            UseDIAttribute attribute = implementationType.GetCustomAttribute<UseDIAttribute>();
            ////获取类实现的所有接口
            //Type[] types = ImplementationType.GetInterfaces();
            List<Type> types = attribute.GetTargetTypes();
            var lifetime = attribute.Lifetime;
            if (types.Count == 0)
                services.Add(new ServiceDescriptor(implementationType, implementationType, lifetime));
            else
                //遍历类实现的每一个接口
                foreach (var type in types)
                {
                    services.Add(new ServiceDescriptor(type, implementationType, lifetime));
                    services.Add(new ServiceDescriptor(implementationType, implementationType, lifetime));
                }
        }

        /// <summary>
        /// 根据程序集的名字获取程序集中所有的类型集合
        /// </summary>
        /// <param name="aassemblyName">程序集名字</param>
        /// <returns>类型集合</returns>
        public static Type[] GetTypesByAssemblyName(String aassemblyName)
        {
            Assembly assembly = Assembly.Load(aassemblyName);
            return assembly.GetTypes();
        }


        #region 将程序集中的所有符合条件的类型全部注册到 IServiceCollection 中 重载1
        /// <summary>
        /// 将程序集中的所有符合条件的类型全部注册到 IServiceCollection 中
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        /// <param name="aassemblyName">程序集名字</param>
        public static void AutoRegisterServicesFromAssembly(this IServiceCollection services, string aassemblyName)
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services) + "为空");

            //根据程序集的名字 获取程序集中所有的类型
            Type[] types = GetTypesByAssemblyName(aassemblyName);
            //过滤上述程序集 首先按照传进来的条件进行过滤 接着要求Type必须是类，而且不能是抽象类
            IEnumerable<Type> _types = types.Where(t => t.IsClass && !t.IsAbstract);
            foreach (var t in _types)
            {
                UseDIAttribute attribute = t.GetCustomAttribute<UseDIAttribute>();
                if (attribute != null)
                    services.AutoRegisterService(t);
            }
        }
        #endregion

        #region 将程序集中的所有符合条件的类型全部注册到 IServiceCollection 中 重载2
        /// <summary>
        /// 将程序集中的所有符合条件的类型全部注册到 IServiceCollection 中
        /// </summary>
        /// <param name="services">IServiceCollection</param>
        /// <param name="aassemblyName">程序集名字</param>
        /// <param name="wherelambda">过滤类型的表达式</param>
        public static void AutoRegisterServicesFromAssembly(this IServiceCollection services,
            string aassemblyName, Func<Type, bool> wherelambda)
        {
            if (services == null)
                throw new ArgumentNullException(nameof(services) + "为空");

            //根据程序集的名字 获取程序集中所有的类型
            Type[] types = GetTypesByAssemblyName(aassemblyName);
            //过滤上述程序集 首先按照传进来的条件进行过滤 接着要求Type必须是类，而且不能是抽象类
            IEnumerable<Type> _types = types.Where(wherelambda).Where(t => t.IsClass && !t.IsAbstract);
            foreach (var t in _types)
            {
                UseDIAttribute attribute = t.GetCustomAttribute<UseDIAttribute>();
                if (attribute != null)
                    services.AutoRegisterService(t);
            }
        }
        #endregion
    }
}
